package helper

import (
	"bufio"
	"errors"
	"github.com/axgle/mahonia"
	"github.com/saintfish/chardet"
	"github.com/slclub/go-tips/stringbyte"
	"golang.org/x/text/encoding/unicode"
	"golang.org/x/text/transform"
	"io"
	"io/ioutil"
	"mime/multipart"
)

// UTF-8 GB-18030 ISO-8859-1 UTF-16BE UTF-16LE UTF-32BE UTF-32LE Big5
const (
	CONST_CHARSET_UTF_8    = "UTF-8"
	CONST_CHARSET_UTF_16BE = "UTF-16BE"
	CONST_CHARSET_UTF_16LE = "UTF-16LE"
)

func ConvertEncodeString(src []byte, srcCode string, tagCode string) []byte {
	srcCoder := mahonia.NewDecoder(srcCode)

	srcResult := srcCoder.ConvertString(stringbyte.BytesToString(src))

	tagCoder := mahonia.NewDecoder(tagCode)

	_, cdata, _ := tagCoder.Translate(stringbyte.StringToBytes(srcResult), true)

	return cdata

}

func CheckStringEncoding(src string) (string, error) {
	rawBytes := stringbyte.StringToBytes(src)
	detector := chardet.NewTextDetector()
	charset, err := detector.DetectBest(rawBytes)
	if err != nil {
		return "", err
	}
	return charset.Charset, nil
}

func CheckReaderEncoding(srcio io.Reader) (string, error) {
	reader := bufio.NewReader(srcio)
	str, readerErr := reader.ReadString('\n')
	if readerErr == io.EOF {
		return CONST_CHARSET_UTF_8, errors.New("Not valide io.reader")
	}
	rawBytes := stringbyte.StringToBytes(str)
	detector := chardet.NewTextDetector()
	charset, err := detector.DetectBest(rawBytes)
	if err != nil {
		return "", err
	}
	return charset.Charset, nil
}

func ConvertToTargetCode(src string, targetCode string) string {
	t, _ := CheckStringEncoding(src)
	if t == "" {
		return ""
	}
	data, err := ConvertUTF16(stringbyte.StringToBytes(src), t)
	if err == nil {
		return stringbyte.BytesToString(data)
	}
	return stringbyte.BytesToString(ConvertEncodeString(stringbyte.StringToBytes(src), t, targetCode))
}

func ConvertUTF16(bs []byte, srcDecode string) ([]byte, error) {
	//bs_UTF16LE, _, _ := transform.Bytes(unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder(), []byte("测试"))
	//bs_UTF16BE, _, _ := transform.Bytes(unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM).NewEncoder(), []byte("测试"))
	switch srcDecode {
	case CONST_CHARSET_UTF_16BE:
		rtn, _, _ := transform.Bytes(unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM).NewDecoder(), bs)
		return rtn, nil
	case CONST_CHARSET_UTF_16LE:
		rtn, _, _ := transform.Bytes(unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewDecoder(), bs)
		return rtn, nil
	}
	return []byte{}, errors.New("not a valide file code")
}

func ConvertFileEncoding(file multipart.File, targetCode string) ([]byte, error) {
	bt, err := CheckReaderEncoding(file)
	if err != nil {
		return []byte{}, err
	}
	file.Seek(0, io.SeekStart)
	bss, err := ioutil.ReadAll(file)
	bs := ConvertEncodeString((bss), bt, targetCode)
	return BomUTF8Cut(bs), nil
}

func BomUTF8Cut(data []byte) []byte {
	if len(data) < 3 {
		return data
	}
	if data[0] == 239 && data[1] == 187 && data[2] == 191 {
		return data[3:]
	}
	return data
}

//var (
//	// Alias for charsets.
//	charsetAlias = map[string]string{
//		"HZGB2312": "HZ-GB-2312",
//		"hzgb2312": "HZ-GB-2312",
//		"GB2312":   "HZ-GB-2312",
//		"gb2312":   "HZ-GB-2312",
//	}
//)

// Supported returns whether charset <charset> is supported.
//func Supported(charset string) bool {
//	return getEncoding(charset) != nil
//}
//
//// Convert converts <src> charset encoding from <srcCharset> to <dstCharset>,
//// and returns the converted string.
//// It returns <src> as <dst> if it fails converting.
//func Convert(dstCharset string, srcCharset string, src string) (dst string, err error) {
//	if dstCharset == srcCharset {
//		return src, nil
//	}
//	dst = src
//	// Converting <src> to UTF-8.
//	if srcCharset != "UTF-8" {
//		if e := getEncoding(srcCharset); e != nil {
//			tmp, err := ioutil.ReadAll(
//				transform.NewReader(bytes.NewReader([]byte(src)), e.NewDecoder()),
//			)
//			if err != nil {
//				return "", fmt.Errorf("%s to utf8 failed. %v", srcCharset, err)
//			}
//			src = string(tmp)
//		} else {
//			return dst, errors.New(fmt.Sprintf("unsupport srcCharset: %s", srcCharset))
//		}
//	}
//	// Do the converting from UTF-8 to <dstCharset>.
//	if dstCharset != "UTF-8" {
//		if e := getEncoding(dstCharset); e != nil {
//			tmp, err := ioutil.ReadAll(
//				transform.NewReader(bytes.NewReader([]byte(src)), e.NewEncoder()),
//			)
//			if err != nil {
//				return "", fmt.Errorf("utf to %s failed. %v", dstCharset, err)
//			}
//			dst = string(tmp)
//		} else {
//			return dst, errors.New(fmt.Sprintf("unsupport dstCharset: %s", dstCharset))
//		}
//	} else {
//		dst = src
//	}
//	return dst, nil
//}
//
//// ToUTF8 converts <src> charset encoding from <srcCharset> to UTF-8 ,
//// and returns the converted string.
//func ToUTF8(srcCharset string, src string) (dst string, err error) {
//	return Convert("UTF-8", srcCharset, src)
//}
//
//// UTF8To converts <src> charset encoding from UTF-8 to <dstCharset>,
//// and returns the converted string.
//func UTF8To(dstCharset string, src string) (dst string, err error) {
//	return Convert(dstCharset, "UTF-8", src)
//}
//
//// getEncoding returns the encoding.Encoding interface object for <charset>.
//// It returns nil if <charset> is not supported.
//func getEncoding(charset string) encoding.Encoding {
//	if c, ok := charsetAlias[charset]; ok {
//		charset = c
//	}
//	if e, err := ianaindex.MIB.Encoding(charset); err == nil && e != nil {
//		return e
//	}
//	return nil
//}
